fs (File system) 是文件系统模块,用于操作文件和目录。
支持同步 (sync) 或者异步 (async/callback) 调用,其中同步调用会阻塞主线程,异步调用不会阻塞。
以读取文件内容为例 (test.txt
)。
sh
hello world
bye bye
下面分别是 3 种写法,日常开发中常用 同步操作
和 Promise形式异步操作
。
```js import fs from 'fs'
// 同步读取 const syncData = fs.readFileSync('./test.txt', 'utf-8') console.log('====sync read====') console.log(syncData)
// 回调形式 异步读取 fs.readFile('./test.txt', 'utf-8', (err, callbackData) => { if (!err) { console.log('====callback read====') console.log(callbackData) } })
// promise形式 异步读取 fs.promises.readFile('./test.txt', 'utf-8').then((promiseData) => { console.log('====promise read====') console.log(promiseData) })
// promise 形式还可以是如下写法(常用) // import fs from 'fs/promises' // fs.readFile('./test.txt', 'utf-8').then((promiseData) => { // console.log('====promise read====') // console.log(promiseData) // }) ```
接下来将介绍一些常用 API,为降低理解成本,主要使用同步方法的形式进行讲解和操作演示。
fs.readFileSync
基础用法如下:
```js import fs from 'fs'
const txtContent = fs.readFileSync('./test.txt', 'utf-8') ```
以二进制形式读取,操作。
```js const buf = fs.readFileSync('./test.txt')
// 打印Buffer大小 console.log(buf.length) // 修改前2个字符 buf.write('gg')
// 输出修改后的内容 console.log(buf.toString()) ```
基础用法如下 fs.writeFileSync
:
js
fs.writeFileSync('./newTest.txt', 'hello world')
写入二进制文件 (读取一个图片,然后输出到一个新的位置)。
```js // 读取一个图片 const imgBuf = fs.readFileSync('./logo.png') console.log('isBuffer', Buffer.isBuffer(imgBuf), 'bufferSize', imgBuf.length)
// 写入到新文件 fs.writeFileSync('newLogo.png', imgBuf, 'binary') ```
通过 fs.statSync
获取文件或者目录的基本信息。
```js import fs from 'fs'
console.log(fs.statSync('./test.txt')) console.log(fs.statSync('./test-dir')) ```
返回的对象属性如下。
常用字段的意义如下。
| 属性名 | 描述 | | ----------- | ---------------------------------------------------- | | dev | 设备 ID,表示该文件所在的设备。 | | mode | 文件权限,包括读、写、执行等权限。 | | nlink | 硬链接数。 | | uid | 用户 ID,表示该文件所属的用户。 | | gid | 组 ID,表示该文件所属的组。 | | rdev | 设备类型,表示该文件所属设备的类型。 | | blksize | 块大小,表示该文件所属设备的块大小。 | | ino | inode 号,表示该文件的 inode 编号。 | | size | 该文件的大小,以字节为单位。 | | blocks | 该文件占用的总块数。 | | atimeMs | 最后访问时间,以毫秒为单位。 | | mtimeMs | 最后修改时间,以毫秒为单位。 | | ctimeMs | 最后状态改变时间,以毫秒为单位。 | | birthtimeMs | 创建时间,以毫秒为单位。 | | atime | 最后访问时间的格式化字符串,通常为 “YYYY-MM-DDTHH:MM:SS.mmmzz” 的形式。 | | mtime | 最后修改时间的格式化字符串,通常为 “YYYY-MM-DDTHH:MM:SS.mmmzz” 的形式。 | | ctime | 最后状态改变时间的格式化字符串,通常为 “YYYY-MM-DDTHH:MM:SS.mmmzz” 的形式。 | | birthtime | 创建时间的格式化字符串,通常为 “YYYY-MM-DDTHH:MM:SS.mmmzz” 的形式。 |
返回的对象上还包含可直接调用的方案,用于判断文件类型。
```js const fileInfo = fs.statSync('./test.txt') // 判断是文件还是目录 console.log(fileInfo.isFile(), fileInfo.isDirectory())
const dirInfo = fs.statSync('./test-dir') // 判断是文件还是目录 console.log(dirInfo.isFile(), dirInfo.isDirectory())
try { // 查询一个不存在的文件/目录信息(会抛出异常,需要自行捕获) fs.statSync('not_exist.txt') } catch (e) { console.log('文件不存在') } ```
使用 fs.appendFileSync
向文件末尾追加写入内容。
```js // 引入文件系统模块 import fs from 'fs';
// 使用 fs.appendFileSync() 方法向指定文件追加内容 // 参数1:指定文件路径 // 参数2:要追加的内容 fs.appendFileSync('test.txt', 'Hello World2!'); ```
fs.renameSync
方法用于文件移动,当然也可以是重命名文件。
下面是文件重命名示例。
```js import fs from 'fs'
fs.renameSync('test.txt', 'test2.txt') ```
下面是移动文件示例。
js
fs.renameSync('test2.txt', 'test-dir/test2.txt')
fs.unlinkSync
和 fs.rmSync
都可用于单文件删除。
```js import fs from 'fs'
fs.unlinkSync('test-dir/test2.txt') // fs.rmSync('test-dir/test2.txt') ```
当然后者还支持删除目录,递归删除子文件/目录等。
js
// 删除 test-dir 目录(包含其子文件)
fs.rmSync('test-dir', { recursive: true })
通过 fs.readdirSync
获取目录下的文件信息。
```js import fs from 'fs'
const files = fs.readdirSync('test-dir')
console.log(files) ```
默认情况下只会返回名称。
可通过指定第二个参数 withFileTypes:true
使返回结果包含类型。
js
const files2 = fs.readdirSync('test-dir', { withFileTypes: true })
console.log(files2.map((f) => ({ name: f.name, isDirectory: f.isDirectory() })))
使用 fs.mkdirSync
创建目录,可通过设置 recursive:true
来递归创建多级目录。
js
fs.mkdirSync('test-dir/a/b/c/d', { recursive: true })
可以使用 fs.rmdirSync
删除目标目录,recursive: true
表明删除包含其子目录。
js
fs.rmdirSync('test-dir/a', { recursive: true })
也可以使用上面提到的 fs.rmSync
。
js
fs.rmSync('test-dir/a', { recursive: true })
利用 fs.watch
即可实现。
js
import fs from 'fs'
// 监听当前目录下所有的文件和子目录中的文件
fs.watch('./', { recursive: true }, (eventType, filename) => {
console.log(`File '${filename}' has changed: ${eventType}`)
})
上面主要阐述了日常开发中常用到的一些方法,下面主要介绍 1 个实际开发中常用的例子。
获取指定目录下所有文件的绝对路径。
利用 fs.readdirSync
和 path.resolve
即可实现。
```js import fs from 'fs' import path from 'path'
function getAllFiles(dirPath, arrayOfFiles) { const files = fs.readdirSync(dirPath, { withFileTypes: true })
arrayOfFiles = arrayOfFiles || []
files.forEach((file) => { if (file.isDirectory()) { arrayOfFiles = getAllFiles(path.resolve(dirPath, file.name), arrayOfFiles) } else { arrayOfFiles.push(path.resolve(dirPath, file.name)) } })
return arrayOfFiles }
// 获取当前目录下所有文件 console.log(getAllFiles('./')) ```
下面是运行结果。
本小节先简单介绍了三种调用文件系统 API 的方式:
fs.readFileSync
,会阻塞主线程;fs.promises.readFile
,fs.readFile
,不会阻塞主线程。日常使用中推荐使用 fs/promise
的方式。
紧接着分别介绍了文件和目录的常规的 CRUD 方法,
最后综合运用上面介绍的方法,解决一个常见场景问题获取指定目录下所有文件的绝对路径 。